package com.cwl.controller;

import com.cwl.constant.FileConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
public class UploadController {

    /**
     * author cwl
     */
    @PostMapping("/upload")
    public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setCharacterEncoding(FileConstant.UTF8);
        Integer schunk = null;//当前分片
        Integer schunks = null;//总分片数
        String name = null;//文件名
        int size = 0;//分片大小
        String uploadPath = "D:\\fileItem";//分片存储路径
        BufferedOutputStream os = null;//合并文件的流
        BufferedInputStream is = null;//合并文件的流
        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setSizeThreshold(1024);//缓冲区，因为文件要先读到内存中在写入硬盘
            factory.setRepository(new File(uploadPath));//硬盘存储的路径
            ServletFileUpload upload = new ServletFileUpload(factory);//用于解析request
            upload.setFileSizeMax(1024L * 1024 * 1024);//设置单文件最多大小
            upload.setSizeMax(10L * 1024 * 1024 * 1024);//设置总文件最大大小
            List<FileItem> fileItems = upload.parseRequest(request);//可以把它解析成一个特定对象，然后处理文件就会变得很方便
            for (FileItem fileItem : fileItems) {
                if (fileItem.isFormField()) {//如果是true表示表单域（非文件域）
                    if ("chunk".equals(fileItem.getFieldName())) {//取出分片信息
                        schunk = Integer.parseInt(fileItem.getString(FileConstant.UTF8));
                        log.debug("分片{}请求过来了---------------", schunk);
                    }
                    if ("chunks".equals(fileItem.getFieldName())) {//取出分片信息
                        schunks = Integer.parseInt(fileItem.getString(FileConstant.UTF8));
                    }
                    if ("name".equals(fileItem.getFieldName())) {//取出分片信息
                        name = fileItem.getString(FileConstant.UTF8);
                    }
                } else {//如果是true表示文件域
                    String tempFileName = name;//临时目录，如果没有分片（就只有一个文件，那么这个文件名就是上传的文件名）
                    if (name != null) {
                        if (schunk != null) {
                            tempFileName = schunk + "_" + name;
                        }
                        File tempFile = new File(uploadPath, tempFileName);
                        if (!tempFile.exists()) {//不存在才需要写,这里是断点续传的关键（网络中断时，总共有10个分片，前面已经传了5个分片，这个时候遍历文件分片时发现前面的分片已经存在； 那么就不用重复写入；
                            fileItem.write(tempFile);
                        }
                    }
                }
            }
            //合并分片
            if (schunk != null) {//有分片才合并，并且不是每个文件过来都合并
                if (schunk == schunks - 1) {//最后一个分片过来的时候才合并,判断是否为最后一个分片
                    //写文件
                    log.debug("最后一块来了");
                    assert name != null;
                    File tempFile = new File(uploadPath, name);
                    os = new BufferedOutputStream(new FileOutputStream(tempFile));
                    //找出所有的文件
                    for (int i = 0; i < schunks; i++) {
                        File file = new File(uploadPath, i + "_" + name);
                        while (!file.exists()) {//如果文件不存在，因为并发的原因，最后这一块分片有可能比前面的分片更早被传完，所以这里自旋判断
                            log.info("自旋等待");
                            TimeUnit.MILLISECONDS.sleep(100);
                        }
                        byte[] bytes = FileUtils.readFileToByteArray(file);//将文件转成字节数组
                        os.write(bytes);
//                        byte[] bytes = new byte[1024];
//                        int len;
//                        is = new BufferedInputStream(new FileInputStream(file));
//                        while (-1 != (len = is.read(bytes))) {
//                            os.write(bytes, 0, len);
//                        }
                        os.flush();
//                        is.close();
                        boolean delete = file.delete();
                        response.getWriter().write("文件{}合并成功" + i + "_" + name);
                        log.info("文件{}合并成功", (i + "_" + name));
                    }
                    log.info("文件{}上传成功", name);
                }

            }
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
                if (null != os) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        ;

    }

}
